package com.yykj.prescription.server.service.impl;

import com.yykj.common.enums.ResultEnums;
import com.yykj.common.exception.NormalException;
import com.yykj.common.util.LocalObjectUtil;
import com.yykj.common.util.PageRequestUtil;
import com.yykj.common.vo.PageData;
import com.yykj.prescription.client.enums.PrescriptionType;
import com.yykj.prescription.server.dto.template.*;
import com.yykj.prescription.server.model.entity.template.PrescriptionTemplate;
import com.yykj.prescription.server.model.entity.template.TemplateDetailChina;
import com.yykj.prescription.server.model.entity.template.TemplateDetailChinaInfo;
import com.yykj.prescription.server.model.entity.template.TemplateDetailWest;
import com.yykj.prescription.server.model.repository.template.PrescriptionTemplateRepository;
import com.yykj.prescription.server.model.repository.template.TemplateDetailChinaInfoRepository;
import com.yykj.prescription.server.model.repository.template.TemplateDetailChinaRepository;
import com.yykj.prescription.server.model.repository.template.TemplateDetailWestRepository;
import com.yykj.prescription.server.service.TemplateService;
import com.yykj.prescription.server.vo.TemplateListVO;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.transaction.Transactional;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
@Transactional
public class TemplateServiceImpl implements TemplateService {

    @Override
    public void createPrescriptionTemplate(TemplateCreateDTO editDTO) {
        PrescriptionTemplate template;
        if (editDTO.getType() == null) {
            throw new NormalException(ResultEnums.PARAM_ERROR);
        }
        if (PrescriptionType.china.equals(editDTO.getType())) {
            if (editDTO.getChinaUseInfo() == null) {
                throw new NormalException(ResultEnums.PARAM_ERROR, "中药用法用量方式");
            }
        }

        template = new PrescriptionTemplate();
        BeanUtils.copyProperties(editDTO, template);
        template.setType(editDTO.getType());

        template = templateRepository.save(template);

        if (PrescriptionType.china.equals(editDTO.getType())) {
            TemplateDetailChinaInfo info = new TemplateDetailChinaInfo();
            BeanUtils.copyProperties(editDTO.getChinaUseInfo(), info);
            info.setTempId(template.getId());
            chinaInfoRepository.save(info);
        }
        saveOneAndRemoveOther(template.getId(), template.getType(), editDTO.getChinaList(), editDTO.getWestList());
    }

    @Override
    public void deleteTemplate(Integer id) {
        PrescriptionTemplate template = templateRepository.findById(id).orElseThrow(() -> new NormalException(ResultEnums.NOT_EXIST));
        if (PrescriptionType.china.equals(template.getType())) {
            List<TemplateDetailChina> allByTempId = chinaRepository.findAllByTempId(template.getId());
            chinaRepository.deleteAll(allByTempId);
            chinaInfoRepository.deleteById(id);
        } else {
            List<TemplateDetailWest> allByTempId1 = westRepository.findAllByTempId(template.getId());
            westRepository.deleteAll(allByTempId1);
        }
        templateRepository.delete(template);

    }

    @Override
    public void editPrescriptionTemplate(TemplateEditDTO editDTO) {
        PrescriptionTemplate template = templateRepository.findById(editDTO.getId()).orElseThrow(() -> new NormalException(ResultEnums.NOT_EXIST));
        template.setTempName(editDTO.getTempName());
        templateRepository.save(template);
        if (PrescriptionType.china.equals(template.getType()) && editDTO.getChinaUseInfo() != null) {
            TemplateDetailChinaInfo chinaInfo = chinaInfoRepository.findById(template.getId()).orElseThrow(() -> new NormalException(ResultEnums.NOT_EXIST));
            LocalObjectUtil.copyNullValue(editDTO.getChinaUseInfo(), chinaInfo);
            chinaInfoRepository.save(chinaInfo);
        }

    }

    @Override
    public void deleteTemplateDetail(TemplateDetailDeleteDTO detailDeleteDTO) {
        PrescriptionTemplate template = templateRepository.findById(detailDeleteDTO.getTempId()).orElseThrow(() -> new NormalException(ResultEnums.NOT_EXIST));
        if (PrescriptionType.china.equals(template.getType())) {
            chinaRepository.deleteById(detailDeleteDTO.getDetailId());
        } else {
            westRepository.deleteById(detailDeleteDTO.getDetailId());
        }
    }

    @Override
    public void editPrescriptionTemplateDetail(TemplateDetailEditDTO addDTO) {
        PrescriptionTemplate template = templateRepository.findById(addDTO.getTempId()).orElseThrow(() -> new NormalException(ResultEnums.NOT_EXIST));

        saveOneAndRemoveOther(template.getId(), template.getType(), addDTO.getChinaList(), addDTO.getWestList());
        if (PrescriptionType.china.equals(addDTO.getType())) {
            TemplateDetailChinaInfo info = new TemplateDetailChinaInfo();
            BeanUtils.copyProperties(addDTO.getChinaUseInfo(), info);
            info.setTempId(template.getId());
            chinaInfoRepository.save(info);
        }
    }

    @Override
    public PageData<TemplateListVO> listTemplate(TemplateSearchDTO templateSearchDTO) {
        if (templateSearchDTO == null || templateSearchDTO.getType() == null) {
            throw new NormalException(ResultEnums.PARAM_ERROR);
        }
        Sort sort = Sort.by(Sort.Order.desc("addTime"), Sort.Order.desc("cnt"));
        if (templateSearchDTO.getSortType() !=null && templateSearchDTO.getSortType() == 2) {
            sort = Sort.by(Sort.Order.desc("cnt"), Sort.Order.desc("addTime"));
        }
        PageRequest pageRequest = PageRequest.of(PageRequestUtil.getPage(templateSearchDTO.getCurrentPage()),
                PageRequestUtil.getSize(templateSearchDTO.getPageSize()), sort);
        Page<PrescriptionTemplate> tempList = templateRepository.findAll(generateSpecification(templateSearchDTO), pageRequest);
        List<Integer> tempIds = new ArrayList<>();
        for (PrescriptionTemplate template : tempList) {
            tempIds.add(template.getId());
        }
        Map<Integer, List<TemplateDetailChina>> chinaMap = new HashMap<>();
        Map<Integer, List<TemplateDetailWest>> westMap = new HashMap<>();
        Map<Integer, TemplateDetailChinaInfo> chinaInfoMap = new HashMap<>();
        if (PrescriptionType.china.equals(templateSearchDTO.getType())) {
            List<TemplateDetailChina> chinaList = chinaRepository.findAllByTempIdIn(tempIds);
            for (TemplateDetailChina china : chinaList) {
                List<TemplateDetailChina> chinaMapList = chinaMap.getOrDefault(china.getTempId(), new ArrayList<>());
                chinaMapList.add(china);
                chinaMap.put(china.getTempId(), chinaMapList);
            }
            List<TemplateDetailChinaInfo> chinaInfoList = chinaInfoRepository.findAllByTempIdIn(tempIds);
            for (TemplateDetailChinaInfo info : chinaInfoList) {
                chinaInfoMap.put(info.getTempId(), info);
            }

        } else {
            List<TemplateDetailWest> westList = westRepository.findAllByTempIdIn(tempIds);
            for (TemplateDetailWest west : westList) {
                List<TemplateDetailWest> westMapList = westMap.getOrDefault(west.getTempId(), new ArrayList<>());
                westMapList.add(west);
                westMap.put(west.getTempId(), westMapList);
            }
        }
        PageData<TemplateListVO> result = new PageData<>();
        List<TemplateListVO> voList = new ArrayList<>();
        for (PrescriptionTemplate template : tempList) {
            TemplateListVO vo = new TemplateListVO();
            BeanUtils.copyProperties(template, vo);
            vo.setChinaList(chinaMap.get(template.getId()));
            vo.setWestList(westMap.get(template.getId()));
            vo.setChinaUseInfo(chinaInfoMap.get(template.getId()));
            voList.add(vo);
        }
        result.setList(voList);
        result.setTotalPage(tempList.getTotalPages());
        result.setTotalCount(tempList.getTotalElements());
        result.setCurrentPage(PageRequestUtil.getPage(templateSearchDTO.getCurrentPage()) + 1);
        result.setPageSize(PageRequestUtil.getSize(templateSearchDTO.getPageSize()));

        return result;
    }

    @Override
    public void increaseUseCnt(String doctorId, List<Integer> tempIds) {
        if (tempIds.size() > 0) {
            List<PrescriptionTemplate> list = templateRepository.findAllByIdIn(tempIds);
            for (PrescriptionTemplate template : list) {
                template.setCnt(template.getCnt() + 1);
            }
            templateRepository.saveAll(list);
        }
    }

    private Specification<PrescriptionTemplate> generateSpecification(TemplateSearchDTO searchDTO) {
        return new Specification<PrescriptionTemplate>() {
            @Override
            public Predicate toPredicate(Root<PrescriptionTemplate> root, CriteriaQuery<?> query, CriteriaBuilder builder) {
                List<Predicate> predicateList = new ArrayList<>();
                if (!StringUtils.isEmpty(searchDTO.getTempName())) {
                    predicateList.add(builder.like(root.get("tempName"), "%" + searchDTO.getTempName() + "%"));
                }
                if (!StringUtils.isEmpty(searchDTO.getDoctorId())) {
                    predicateList.add(builder.equal(root.get("doctorId"), searchDTO.getDoctorId()));
                }
                if (searchDTO.getHospitalId() != null && searchDTO.getHospitalId() > 0) {
                    predicateList.add(builder.equal(root.get("hospitalId"), searchDTO.getHospitalId()));
                }

                if (searchDTO.getType() != null) {
                    predicateList.add(builder.equal(root.get("type"), searchDTO.getType()));
                }
                Predicate[] predicates = new Predicate[predicateList.size()];
                return query.where(predicateList.toArray(predicates)).getRestriction();
            }
        };
    }


    private void saveOneAndRemoveOther(Integer tempId, PrescriptionType type, List<TemplateDetailChina> chinaList, List<TemplateDetailWest> westList) {
        List<TemplateDetailWest> westRemoveList = westRepository.findAllByTempId(tempId);
        List<TemplateDetailChina> chinaRemoveList = chinaRepository.findAllByTempId(tempId);

        westRepository.deleteAll(westRemoveList);
        chinaRepository.deleteAll(chinaRemoveList);

        if (PrescriptionType.china.equals(type)) {
            for (TemplateDetailChina china : chinaList) {
                china.setTempId(tempId);
            }
            chinaRepository.saveAll(chinaList);
        } else {
            for (TemplateDetailWest west : westList) {
                west.setTempId(tempId);
            }
            westRepository.saveAll(westList);
        }
    }

    private PrescriptionTemplateRepository templateRepository;

    private TemplateDetailChinaRepository chinaRepository;

    private TemplateDetailWestRepository westRepository;

    private TemplateDetailChinaInfoRepository chinaInfoRepository;

    @Autowired
    public void setTemplateRepository(PrescriptionTemplateRepository templateRepository) {
        this.templateRepository = templateRepository;
    }

    @Autowired
    public void setChinaRepository(TemplateDetailChinaRepository chinaRepository) {
        this.chinaRepository = chinaRepository;
    }

    @Autowired
    public void setWestRepository(TemplateDetailWestRepository westRepository) {
        this.westRepository = westRepository;
    }

    @Autowired
    public void setChinaInfoRepository(TemplateDetailChinaInfoRepository chinaInfoRepository) {
        this.chinaInfoRepository = chinaInfoRepository;
    }
}
